home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_87 / sounddev.pas < prev    next >
Pascal/Delphi Source File  |  1995-01-01  |  24KB  |  682 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:         SoundDevices                                               }
  4. {                                                                            }
  5. { DESCRIPTION:    Implements a common interface to access the different      }
  6. {                 sampled audio devices possible on a PC, wether they work   }
  7. {                 with DMA or polling.                                       }
  8. {                                                                            }
  9. { AUTHOR:         Juan Carlos Arévalo Baeza                                  }
  10. {                                                                            }
  11. { MODIFICATIONS:  Nobody yet.                                                }
  12. {                                                                            }
  13. { HISTORY:        xx-May-1992 Conception.                                    }
  14. {                 xx-Jun-1992 Development.                                   }
  15. {                 21-Jul-1992 Documentation (this mess).                     }
  16. {                 07-Oct-1992 Redo from start :-( (DMA Affairs).             }
  17. {                                                                            }
  18. { (C) 1992 VangeliSTeam                                                      }
  19. {____________________________________________________________________________}
  20.  
  21. UNIT SoundDevices;
  22.  
  23. INTERFACE
  24.  
  25. USES SongElements;
  26.  
  27.  
  28.  
  29. {----------------------------------------------------------------------------}
  30. { Device configuration definitions.                                          }
  31. {____________________________________________________________________________}
  32.  
  33. TYPE
  34.   TDevName = STRING[50];                    { Name/description of device.                                    }
  35.   TDevID   = STRING[20];                    { Device identification string.                                  }
  36.  
  37.   TProc            = PROCEDURE;                       { Generic procedure without parameters.                }
  38.   TNameProc        = FUNCTION             : TDevName; { Procedure that returns the name of a device.         }
  39.   TInitDevProc     = PROCEDURE (Hz: WORD);            { Device initialisation procedure.                     }
  40.   TChgHzProc       = PROCEDURE (Hz: WORD);            { Sample rate change procedure.                        }
  41.   TGetRealFreqProc = FUNCTION  (Hz: WORD) : WORD;     { Returns the real sampling freq. when Hz is selected. }
  42.   TDetectProc      = FUNCTION             : BOOLEAN;  { Device autodetection procedure.                      }
  43.  
  44. TYPE
  45.   PSoundDevice = ^TSoundDevice;   { Device record for including in a linked list.                            }
  46.   TSoundDevice = RECORD
  47.     DevID         : TDevID;       { Device ID string.                                                        }
  48.     DMA           : BOOLEAN;      { TRUE if the device uses DMA output (shouldn't be needed).                }
  49.  
  50.     Name            : TNameProc;        { Device name.                                                       }
  51.     Autodetect      : TDetectProc;      { Autodetection procedure.                                           }
  52.     InitRut         : TInitDevProc;     { Initialisation procedure.                                          }
  53.     ChgHzProc       : TChgHzProc;       { Sample rate change procedure.                                      }
  54.     GetRealFreqProc : TGetRealFreqProc; { Real sampling freq.                                                }
  55.     TimerHandler,                       { INT 8 handler for the device.                                      }
  56.     PollRut         : TProc;            { Routine to be executed for active polling (hand made).             }
  57.     EndRut          : TProc;            { Device closing procedure.                                          }
  58.  
  59.     Next          : PSoundDevice;     { Next record in the list.                                             }
  60.   END;
  61.  
  62. CONST
  63.   NumDevices   : BYTE         = 0;   { Count of the number of installed devices.    }
  64.   ActiveDevice : PSoundDevice = NIL; { Device being used right now.                 }
  65.  
  66.  
  67.  
  68.  
  69. {----------------------------------------------------------------------------}
  70. { Device Stack.                                                              }
  71. {____________________________________________________________________________}
  72.  
  73. CONST
  74.   DevStkSize = 500;
  75. VAR
  76.   DevStack : ARRAY[1..DevStkSize] OF BYTE;
  77.   DevSS    : WORD;
  78.   DevSP    : WORD;
  79.  
  80. {----------------------------------------------------------------------------}
  81. { Sample buffers definition.                                                 }
  82. {____________________________________________________________________________}
  83.  
  84. TYPE
  85.   TDataType = (dtShortInt, dtInteger);        { Data type of the samples. }
  86.  
  87.   TIntBuff   = ARRAY[0..32760] OF INTEGER;    { Data types for big arrays. }
  88.   TShortBuff = ARRAY[0..65520] OF SHORTINT;
  89.  
  90.   PIntBuff   = ^TIntBuff;                     { Idem. }
  91.   PShortBuff = ^TShortBuff;
  92.  
  93.   PSampleBuffer = ^TSampleBuffer;   { PCM Buffer. }
  94.   TSampleBuffer = RECORD
  95.     InUse    : BOOLEAN;             { TRUE while it's being used by the device.       }
  96.     NSamples,                       { Size of the buffer in samples.                  }
  97.     RateHz   : WORD;                { Sampling frequency.                             }
  98.     Channels : BYTE;                { 1 or 4, channels contained in the buffer.       }
  99.     CASE DataType : TDataType OF
  100.       dtInteger:  ( IData : PIntBuff );   { Pointer to the buffer.                    }
  101.       dtShortInt: ( SData : PShortBuff );
  102.   END;
  103.  
  104. CONST
  105.   MaxChannels  = SongElements.MaxChannels;
  106.   Sounding     : POINTER = NIL;  { Buffer that is actually sounding (NON-DMA only). }
  107.   SoundLeft    : WORD    = 0;    { Number of samples left in the buffer.            }
  108.   NumChannels  : BYTE    = 1;    { Number of channels in the buffer.                }
  109.   ChannelIncr  : WORD    = 1;    { Size of one sample in the buffer.                }
  110.  
  111.  
  112.  
  113.  
  114. {----------------------------------------------------------------------------}
  115. { DMA buffers definition.                                                    }
  116. {____________________________________________________________________________}
  117.  
  118. CONST
  119.   DMABufferSize = 5*700;     { Size of the buffers. }
  120.  
  121. VAR
  122. (*
  123.   DMABuffers : ARRAY[1..DMABufferSize*2] OF BYTE; { Physical memory for buffers.                     }
  124.                                                   { Needs 4 to be sure we have 2 countiguous. :-(    }
  125.                                                   { That's the PC-DMA neverending story.             }
  126. *)
  127.  
  128.   DMABufferPtr : POINTER;                         { DMA-fixed ;-) pointers for   }
  129.   DMABuffer    : POINTER;                         { buffers 1 and 2.             }
  130.   DMABufferEnd : WORD;
  131.  
  132.  
  133.  
  134.  
  135. {----------------------------------------------------------------------------}
  136. { Hardware parameters.                                                       }
  137. {____________________________________________________________________________}
  138.  
  139. CONST
  140.   DefaultHz                   = 16000;     { Default sampling rate.                                }
  141.   DeviceIdling      : BOOLEAN = TRUE;      { TRUE if there are no samples sounding.                }
  142.   TimerHz           : WORD    = DefaultHz; { Clock frequency of the INT 8 timer.                   }
  143.   LastHz            : WORD    = 0;         { Older INT 8 frequency (for detecting change).         }
  144.   SoundHz           : WORD    = DefaultHz; { Sampling frequency of the sound.                      }
  145.   DesiredHz         : WORD    = DefaultHz; { Desired sampling frequency of the sound.              }
  146.   SystemClockCount  : WORD    = 0;         { Clock count for calling the original INT 8.           }
  147.   SystemClockIncr   : WORD    = 0;         { Increment for calling the original INT 8.             }
  148.   TimerVal          : WORD    = 0;         { Value given to the INT 8 timer.                       }
  149.   DeviceInitialized : BOOLEAN = FALSE;     { TRUE if a device has already been initialized.        }
  150.   DMAOffset         : WORD    = 1;         { Number of samples to discard in DMA transferences.    }
  151.   HzChanged         : BOOLEAN = FALSE;
  152.   DoEqualice        : BOOLEAN = FALSE;
  153.  
  154.  
  155.  
  156. {----------------------------------------------------------------------------}
  157. { Periodic process.                                                          }
  158. {____________________________________________________________________________}
  159.  
  160. VAR
  161.   PeriodicProc  : TProc;    { Periodic process (normally a music interpreter).           }
  162.  
  163. CONST
  164.   PeriodicHz    : BYTE = 0; { Frequency for calling the periodic process.                }
  165.   PeriodicStart : WORD = 1; { Countdown starting point (NON-DMA only).                   }
  166.   PeriodicCount : WORD = 0; { Countdown.               (idem).                           }
  167.  
  168.  
  169.  
  170.  
  171. {----------------------------------------------------------------------------}
  172. { Buffer provider definitions.                                               }
  173. {____________________________________________________________________________}
  174.  
  175. TYPE
  176.   TAskBufferProc = FUNCTION : PSampleBuffer; { Buffer provider function. }
  177.  
  178. VAR
  179.   AskBufferProc  : TAskBufferProc; { Pointer to the buffer provider.             }
  180.   ActualBuffer,                    { Buffer being used.                          }
  181.   NextBuffer     : PSampleBuffer;  { Buffer that will be used next.              }
  182.   PleaseFallback : WORD{BOOLEAN};        { Set TRUE if there are no buffers available. }
  183.  
  184.  
  185.  
  186.  
  187. {----------------------------------------------------------------------------}
  188. { Functions to be used by devices only.                                      }
  189. {____________________________________________________________________________}
  190.  
  191. FUNCTION  InitDevice   (Device: PSoundDevice) : WORD; { Used to declare a device.                            }
  192. PROCEDURE PollDevice;                                 { Used to manually poll the device, if it is required. }
  193. PROCEDURE CalcTimerData(Hz: WORD);                    { Used to calculate the different Hz variables.        }
  194. PROCEDURE DefaultChgHz (Hz: WORD);                    { Used as a default TChgHzProc.                        }
  195. FUNCTION  GetRealFreq  (Hz: WORD)             : WORD; { Used as a default TRealFreqProc.                     }
  196. PROCEDURE InitTimer;                                  { Used to reinitialise the timer after a freq. change. }
  197. FUNCTION  DoGetBuffer                         : WORD; { Used to get the next buffer prepared.                }
  198. PROCEDURE CSOldInt8;                                  { Used to call the old INT 8 vector.                   }
  199.  
  200.  
  201.  
  202.  
  203. {----------------------------------------------------------------------------}
  204. { Functions to be used by the sound generators only.                         }
  205. {____________________________________________________________________________}
  206.  
  207. PROCEDURE SetDevice      (p: PSoundDevice);                             { Used to initialise a buffer for output.  }
  208. FUNCTION  IndexDevice    (i: WORD)                      : PSoundDevice; { Used to index the devices.               }
  209. FUNCTION  LocateDevice   (ID: STRING)                   : PSoundDevice; { Used to find a given device.             }
  210. PROCEDURE SetPeriodicProc(Proc: TProc; PerSecond: WORD);                { Used to initialise the periodic process. }
  211. PROCEDURE SetBufferAsker (Proc: TAskBufferProc);                        { Used to initialise the buffer asker.     }
  212. PROCEDURE StartSampling;                                                { Used to start the sound output.          }
  213. PROCEDURE EndSampling;                                                  { Used to end the sound output.            }
  214.  
  215.  
  216. PROCEDURE InitSoundDevices;
  217.  
  218.  
  219.  
  220.  
  221. IMPLEMENTATION
  222.  
  223. USES Dos,
  224.      Debugging, Output43;
  225.  
  226.  
  227.  
  228.  
  229. {----------------------------------------------------------------------------}
  230. { Internal data.                                                             }
  231. {____________________________________________________________________________}
  232.  
  233. CONST
  234.   DeviceList       : PSoundDevice = NIL;   { Linked list of all devices.                     }
  235.   OldInt8          : POINTER      = NIL;   { Pointer to the original INT 8.                  }
  236.   IntInstalled     : BOOLEAN      = FALSE; { TRUE if the INT 8 handler is already installed. }
  237.  
  238.  
  239.  
  240.  
  241. {----------------------------------------------------------------------------}
  242. { Null procedures used in the unit.                                          }
  243. {____________________________________________________________________________}
  244.  
  245. PROCEDURE NullProcedure;                  FAR; ASSEMBLER; ASM END;
  246. FUNCTION  NullBufferProc : PSampleBuffer; FAR; BEGIN NullBufferProc := NIL; END;
  247.  
  248.  
  249. PROCEDURE NullInt; ASSEMBLER;
  250.   ASM
  251.         PUSH    AX
  252.         MOV     AL,$20
  253.         OUT     $20,AL
  254.         POP     AX
  255.         IRET
  256.   END;
  257.  
  258.  
  259.  
  260.  
  261. {----------------------------------------------------------------------------}
  262. { A little bit messy, but it implements an easy jump to the original INT 8.  }
  263. {____________________________________________________________________________}
  264.  
  265. PROCEDURE CSOldInt8; ASSEMBLER;
  266.   ASM
  267.         JMP FAR PTR CSOldInt8;
  268.   END;
  269.  
  270. TYPE
  271.   PCSOldInt8 = ^TCSOldInt8;
  272.   TCSOldInt8 = RECORD
  273.     JMP : BYTE;
  274.     Int : POINTER;
  275.   END;
  276.  
  277. VAR
  278.   _CSOldInt8 : PCSOldInt8;
  279.  
  280.  
  281.  
  282.  
  283. {----------------------------------------------------------------------------}
  284. { Periodic process implementation.                                           }
  285. {____________________________________________________________________________}
  286.  
  287. PROCEDURE InitPeriodic;
  288.   BEGIN
  289.     IF PeriodicHz = 0 THEN BEGIN
  290.       PeriodicStart   := 0;
  291.       PeriodicCount   := 0;
  292.       SystemClockIncr := TimerVal;
  293.     END ELSE BEGIN
  294.       PeriodicStart := TimerHz DIV PeriodicHz;
  295.       IF PeriodicStart = 0 THEN PeriodicStart := 1;
  296.       PeriodicCount := 1;
  297.       SystemClockIncr := TimerVal * PeriodicStart;
  298.     END;
  299.   END;
  300.  
  301.  
  302. PROCEDURE SetPeriodicProc(Proc: TProc; PerSecond: WORD);
  303.   BEGIN
  304.     ASM
  305.         PUSHF
  306.         CLI
  307.         LES     BX,[Proc]
  308.         MOV     WORD PTR [PeriodicProc],BX;
  309.         MOV     WORD PTR [PeriodicProc+2],ES;
  310.         POPF
  311.     END;
  312.     PeriodicHz   := PerSecond;
  313.     InitPeriodic;
  314.   END;
  315.  
  316.  
  317.  
  318.  
  319. {----------------------------------------------------------------------------}
  320. { Hardware and interrupt handling procedures.                                }
  321. {____________________________________________________________________________}
  322.  
  323. PROCEDURE OriginalHwTimer; ASSEMBLER;
  324.   ASM
  325.         MOV     AL,54       { Selct timer 0, secuential access and contínuous mode. }
  326.         OUT     43h,AL
  327.         XOR     AL,AL       { Set the counter to 0 (65536). }
  328.         OUT     40h,AL      { Lower byte of the counter.    }
  329.         OUT     40h,AL      { Higher byte.                  }
  330.   END;
  331.  
  332.  
  333. PROCEDURE SetHwTimer(value: WORD); ASSEMBLER;
  334.   ASM
  335.         MOV     AL,54       { Selct timer 0, secuential access and contínuous mode. }
  336.         OUT     43h,AL
  337.         MOV     AX,value
  338.         OUT     40h,AL      { Lower byte of the counter.    }
  339.         XCHG    Ah,AL
  340.         OUT     40h,AL      { Higher byte.                  }
  341.   END;
  342.  
  343.  
  344. PROCEDURE RestoreTimer;
  345.   BEGIN
  346.     IF IntInstalled THEN
  347.       BEGIN
  348.         SetIntVec(8, OldInt8);
  349.         OriginalHwTimer;
  350.         IntInstalled := FALSE;
  351.       END;
  352.   END;
  353.  
  354.  
  355. PROCEDURE InitTimer;
  356.   BEGIN
  357.     InitPeriodic;
  358.  
  359.     IF NOT IntInstalled THEN
  360.       BEGIN
  361.         IntInstalled := TRUE;
  362.         GetIntVec(8, OldInt8);
  363.         _CSOldInt8^.Int := OldInt8;
  364.         SetIntVec(8, @ActiveDevice^.TimerHandler);
  365.       END;
  366.  
  367.     SetHwTimer(TimerVal);
  368.   END;
  369.  
  370.  
  371.  
  372.  
  373. {----------------------------------------------------------------------------}
  374. { Procedures exported for the sound generator.                               }
  375. {____________________________________________________________________________}
  376.  
  377. PROCEDURE StartSampling;
  378.   BEGIN
  379.     IF NOT DeviceInitialized THEN RestoreTimer;
  380.  
  381.     ActualBuffer      := NIL;
  382.     NextBuffer        := NIL;
  383.     SoundLeft         := 0;
  384.     PleaseFallBack    := 0;
  385.     DeviceIdling      := TRUE;
  386.     DMABufferPtr      := DMABuffer;
  387.  
  388.     FillChar(DMABuffer^, DMABufferSize, $80);
  389.  
  390.     IF (ActiveDevice <> NIL) {AND (NOT DeviceInitialized)} THEN
  391.       BEGIN
  392.         ASM CLI END;
  393.  
  394.         DeviceInitialized := TRUE;
  395.  
  396.         ActiveDevice^.InitRut(DesiredHz);
  397.  
  398.         ASM STI END;
  399.       END;
  400.  
  401.   END;
  402.  
  403.  
  404. PROCEDURE EndSampling;
  405.   BEGIN
  406.     IF (ActiveDevice <> NIL) AND DeviceInitialized THEN
  407.       BEGIN
  408.         ASM CLI END;
  409.         ActiveDevice^.EndRut;
  410.         RestoreTimer;
  411.         ASM STI END;
  412.         DeviceInitialized := FALSE;
  413.       END;
  414.   END;
  415.  
  416.  
  417. PROCEDURE SetBufferAsker (Proc: TAskBufferProc);
  418.   BEGIN
  419.     ASM CLI END;
  420.      AskBufferProc := Proc;
  421.     ASM STI END;
  422.   END;
  423.  
  424.  
  425. PROCEDURE SetDevice(p: PSoundDevice);
  426.   BEGIN
  427.     IF p <> NIL THEN
  428.       BEGIN
  429.         IF DeviceInitialized THEN
  430.           BEGIN
  431.             EndSampling;
  432.             ActiveDevice := p;
  433.             StartSampling;
  434.           END
  435.         ELSE
  436.           ActiveDevice := p;
  437.       END;
  438.   END;
  439.  
  440.  
  441. FUNCTION LocateDevice(ID: STRING) : PSoundDevice;
  442.  
  443.   FUNCTION NotInStr(VAR s, ss: STRING) : BOOLEAN;
  444.     VAR
  445.       i : WORD;
  446.     BEGIN
  447.       NotInStr := TRUE;
  448.       IF Length(ss) > Length(s) THEN EXIT;
  449.       FOR i := 1 TO Length(ss) DO
  450.         IF UpCase(s[i]) <> UpCase(ss[i]) THEN EXIT;
  451.       NotInStr := FALSE;
  452.     END;
  453.  
  454.   VAR
  455.     p : PSoundDevice;
  456.   BEGIN
  457.     p := DeviceList;
  458.     WHILE (p <> NIL) AND NotInStr(p^.DevID, ID) DO p := p^.Next;
  459.     LocateDevice := p;
  460.   END;
  461.  
  462.  
  463. FUNCTION IndexDevice(i: WORD) : PSoundDevice;
  464.   VAR
  465.     p : PSoundDevice;
  466.   BEGIN
  467.     p := DeviceList;
  468.     DEC(i);
  469.     WHILE (p <> NIL) AND (i > 0) DO
  470.       BEGIN
  471.         p := p^.Next;
  472.         DEC(i);
  473.       END;
  474.  
  475.     IndexDevice := p;
  476.   END;
  477.  
  478.  
  479.  
  480.  
  481. {----------------------------------------------------------------------------}
  482. { Implementation of some procedures exported to the device controllers.       }
  483. {____________________________________________________________________________}
  484.  
  485. FUNCTION InitDevice(Device: PSoundDevice) : WORD;
  486.   BEGIN
  487.     Device^.Next := DeviceList;
  488.     DeviceList   := Device;
  489.     IF ActiveDevice = NIL THEN SetDevice(Device);
  490.     INC(NumDevices);
  491.   END;
  492.  
  493.  
  494. PROCEDURE PollDevice;
  495.   BEGIN
  496.     ActiveDevice^.PollRut;
  497.   END;
  498.  
  499.  
  500. FUNCTION GetRealFreq(Hz: WORD) : WORD;
  501.   VAR
  502.     i    : WORD;
  503.     NHz1 : WORD;
  504.     NHz2 : WORD;
  505.   BEGIN
  506.     IF Hz = 0 THEN Hz := 1;
  507.     i := 1193180 DIV Hz;
  508.  
  509.     NHz1 := 1193180 DIV  i;
  510.     NHz2 := 1193180 DIV (i + 1);
  511.     IF ABS(INTEGER(NHz1 - Hz)) > ABS(INTEGER(NHz2 - Hz)) THEN NHz1 := NHz2;
  512.  
  513.     GetRealFreq := NHz1;
  514.   END;
  515.  
  516.  
  517. PROCEDURE CalcTimerData(Hz: WORD);
  518.   BEGIN
  519.     Hz := GetRealFreq(Hz);
  520.  
  521.     IF Hz = 0 THEN TimerVal := $FFFF
  522.               ELSE TimerVal := 1193180 DIV Hz;
  523.  
  524.     TimerHz         := 1193180 DIV TimerVal;
  525.     SoundHz         := TimerHz;
  526. {    SystemClockIncr := TimerVal;}
  527.   END;
  528.  
  529.  
  530. PROCEDURE DefaultChgHz(Hz: WORD);
  531.   BEGIN
  532.     CalcTimerData(Hz);
  533.     InitTimer;
  534.   END;
  535.  
  536.  
  537. FUNCTION DoGetBuffer : WORD;
  538.   CONST
  539.     Semaphore : BYTE    = 0;
  540.     Size      : WORD    = 1;
  541.   BEGIN
  542.     DoGetBuffer := 0;
  543.     IF Semaphore > 0 THEN EXIT;
  544.     INC(Semaphore);
  545.  
  546.     IF ActualBuffer <> NIL THEN
  547.       BEGIN
  548.         Size := ActualBuffer^.NSamples;
  549.         ActualBuffer^.InUse := FALSE; { It must be already finished using. }
  550.       END;
  551.  
  552.     ActualBuffer := NextBuffer;
  553.     NextBuffer   := AskBufferProc; { Get the buffer, if there is one. }
  554.  
  555.     IF ActualBuffer = NIL THEN BEGIN     { If there had not been next buffer before. }
  556.       ActualBuffer := NextBuffer;
  557.       IF ActualBuffer <> NIL THEN BEGIN  { If there has just been one more buffer. }
  558.         ActualBuffer^.InUse := TRUE;
  559.         NextBuffer          := AskBufferProc; { Try to get another one. }
  560.       END;
  561.     END;
  562.  
  563.     IF NextBuffer <> NIL THEN
  564.       NextBuffer^.InUse := TRUE;
  565.  
  566.     IF ActualBuffer = NIL THEN
  567.       BEGIN                                  { If there is no buffer :-( }
  568.         IF (Size <> 1) AND (NOT ActiveDevice^.DMA) THEN
  569.           INC(PleaseFallBack);
  570.         SoundLeft      := 0;
  571.         IF NOT ActiveDevice^.DMA THEN
  572.           BEGIN
  573.             PeriodicCount := 1;
  574.             LastHz        := PeriodicHz;
  575.             ActiveDevice^.ChgHzProc(LastHz);
  576.           END;
  577.       END
  578.     ELSE
  579.       BEGIN
  580.         IF (LastHz <> ActualBuffer^.RateHz) THEN BEGIN
  581.           LastHz := ActualBuffer^.RateHz;
  582.           ActiveDevice^.ChgHzProc(LastHz);
  583.  
  584.           HzChanged := TRUE;
  585.  
  586.         END;
  587.         Sounding     := ActualBuffer^.IData;
  588.         SoundLeft    := ActualBuffer^.NSamples;
  589.         NumChannels  := ActualBuffer^.Channels;
  590.         ChannelIncr  := ActualBuffer^.Channels * (ORD(ActualBuffer^.DataType)+1);
  591.  
  592.         IF ActiveDevice^.DMA THEN
  593.           BEGIN
  594.             IF SoundLeft > DMAOffset + 5 THEN
  595.               DEC(SoundLeft, DMAOffset)
  596.             ELSE
  597.               SoundLeft := 5;
  598.           END;
  599.  
  600.  
  601.       END;
  602.  
  603.     DoGetBuffer := SoundLeft;
  604.  
  605.     WriteNum(40, SoundLeft, $70);
  606.  
  607.     DEC(Semaphore);
  608.   END;
  609.  
  610.  
  611.  
  612.  
  613. {----------------------------------------------------------------------------}
  614. { Unit initialisation.                                                       }
  615. {____________________________________________________________________________}
  616.  
  617. PROCEDURE InitSoundDevices;
  618.   TYPE
  619.     PFreeBlock = ^TFreeBlock;
  620.     TFreeBlock =
  621.       RECORD
  622.         Next : PFreeBlock;
  623.         Size : POINTER;
  624.       END;
  625.   VAR
  626.     l        : LONGINT;
  627.     PtrFree  : POINTER;
  628.     OldHPtr  : POINTER;
  629.     p        : PFreeBlock;
  630.     OffsFree : WORD;
  631.   BEGIN
  632.  
  633.     _CSOldInt8 := @CSOldInt8;
  634.  
  635.     PeriodicProc  := NullProcedure;
  636.     AskBufferProc := NullBufferProc;
  637.  
  638.     { Calc. for the DMA buffers. This messes with the heap, but works. }
  639.  
  640.     DMABuffer := HeapPtr;
  641.  
  642.     l := (LONGINT(SEG(DMABuffer^)) SHL 4) + OFS(DMABuffer^); { l = linear address. }
  643.  
  644.     PtrFree  := HeapPtr;
  645.     OffsFree := 0;
  646.  
  647.     IF LONGINT(WORD(l)) + DMABufferSize > 65536 THEN { If address doesn't match,    }
  648.       BEGIN                                          { get an address that matches  }
  649.         OffsFree := 65536 - LONGINT(WORD(l));        { by incrementing to a 64 Kb   }
  650.         l := (l AND $F0000) + $10000;                { boundary.                    }
  651.       END;
  652.  
  653.     DMABuffer    := Ptr((l SHR 4)  AND $F000, WORD(l));
  654.     DMABufferPtr := DMABuffer;
  655.     DMABufferEnd := OFS(DMABuffer^) + DMABufferSize;
  656.  
  657.     OldHPtr      := HeapPtr;
  658.     HeapPtr      := Ptr((l + DMABufferSize + 16) SHR 4, 0); { Manually, allocate the }
  659.     IF OldHPtr = FreeList THEN                              { buffer.                }
  660.       BEGIN
  661.         FreeList   := HeapPtr;
  662.       END
  663.     ELSE
  664.       BEGIN
  665.         p := FreeList;
  666.         WHILE p^.Next <> OldHPtr DO
  667.           p := p^.Next;
  668.         p^.Next := HeapPtr;
  669.       END;
  670.  
  671.     FillChar(HeapPtr^, 8, 0); { Clear the Heap Pointer contents. }
  672.  
  673.     IF OffsFree > 0 THEN              { Update the Heap by freeing  }
  674.       FreeMem(PtrFree, OffsFree);     { manually the unused memory. }
  675.   END;
  676.  
  677.  
  678.  
  679.  
  680. END.
  681.  
  682.